const http = require('http');
const url = require('url');
const crypto = require('crypto');
const path = require('path');
const fs = require('fs/promises');
/**
 * fs/promises模块将所以异步方法进行promisify包装
 *
 * function promisify(fn, ...args){
 *   new Promise((resolve, reject) => {
 *     fn(...args, (err, data) => {
 *       if(err) return reject(err);
 *       resolve(data);
 *     })
 *   })
 * }
 *
 * let fs.readFile = promisify(fs.readFile);
 */

class NodeServer {
  constructor() {
    this.handleRequest = this.handleRequest.bind(this);
  }

  start() {
    const server = http.createServer(this.handleRequest);
    server.listen(8001, () => {
      console.log('Server running at port 8001');
    });
  }

  async handleRequest(req, res) {
    /**
     * 补充API属性和方法：
        //url.parse(req.url) 该方法可以解析url路径为一个对象信息，目的是可以获取get请求url地址携带的参数信息
        // let { pathname, query } = url.parse(req.url);
        // console.log(pathname, query);
        ///public/index.html   name=kevin

        //监听可读流事件 此函数目的是为了获取post请求时发送的body参数信息，但是接收的是buffer流
        // req.on('data', (chunks) => {
        //   console.log(chunks.toString());
        //   //name=kevin&age=28
        // });

        //设置响应状态码和信息
        // res.statusCode = '200';
        // res.statusMessage = 'success';

        //设置响应头信息
        // res.setHeader('Content-Type', 'text/html;chartset=utf8');
     */

    let { pathname } = url.parse(req.url);

    if (pathname === '/favicon.ico') return;
    // console.log(pathname);
    // /public/index.html

    const filePath = path.join(__dirname, '../', pathname);
    // console.log(__dirname, filePath);
    // /Users/kevinlee/front-end/template/node-server-template/src
    // /Users/kevinlee/front-end/template/node-server-template/public/index.html

    //获取请求头信息里的上次修改时间 if-modified-since
    let ifModofiedSince = req.headers['if-modified-since'];
    // console.log(ifModofiedSince);
    //Tue, 11 Apr 2023 16:25:00 GMT

    //获取请求头信息里的上次修改内容的hash值 if-none-match
    let ifNoneMatch = req.headers['if-none-match'];

    try {
      let statObj = await fs.stat(filePath);
      /**
       * console.log(statObj);
       * Stats {
          dev: 16777234,
          mode: 33188,
          nlink: 1,
          uid: 501,
          gid: 20,
          rdev: 0,
          blksize: 4096,
          ino: 10895185,
          size: 270,
          blocks: 8,
          atimeMs: 1681228165319.32,
          mtimeMs: 1681228165297.7073,
          ctimeMs: 1681228165297.7073,
          birthtimeMs: 1681228163710.1008,
          atime: 2023-04-11T15:49:25.319Z,
          mtime: 2023-04-11T15:49:25.298Z,
          ctime: 2023-04-11T15:49:25.298Z,
          birthtime: 2023-04-11T15:49:23.710Z
        }
       */

      //最后修改时间
      let lastModified = statObj.mtime.toGMTString();
      //对首屏文件进行协商缓存设置(index.html)
      res.setHeader('Last-Modified', lastModified);

      //进行对比，如果时间没有变化则缓存，不返回内容。
      //优先执行比对，如果时间不一样，让程序往下执行，就重新文件。
      if (ifModofiedSince === lastModified) {
        res.statusCode = 304;
        return res.end();
      }

      //是否文件时
      if (statObj.isFile()) {
        //文件
        let result = await fs.readFile(filePath);

        //通过md5算法方式来创建hash值，指定的内容为文件读取内容。
        //网络传输字符串会存在丢失问题，需要转码操作，可以转化为base64编码格式。
        const contentHash = crypto
          .createHash('md5')
          .update(result)
          .digest('base64');
        // console.log(contentHash);
        //kHOCxClSl858EY/55HXpzw==

        //设置响应头的`Etag`标志属性的值为当前的`hash`值。
        res.setHeader('Etag', contentHash);

        //对文件内容摘要哈希进行比对。
        if (ifNoneMatch === contentHash) {
          res.statusCode = 304;
          return res.end();
        }

        //过期时间为10秒
        const expiresTimes = new Date(Date.now() + 10 * 1000).toGMTString();
        // console.log(expiresTimes);
        //Tue, 11 Apr 2023 16:35:09 GMT

        // console.log(lastModified);
        //Tue, 11 Apr 2023 16:25:00 GMT

        //方法一： 对静态文件进行强缓存设置 (index.css index.js) 缓存时间为10秒
        res.setHeader('Expires', expiresTimes);

        //方法二： 对静态文件进行强缓存设置 (index.css index.js) 缓存时间为10秒
        res.setHeader('Cache-Control', 'max-age=10');

        //该no-cache字段不缓存
        // res.setHeader('Cache-Control', 'no-cache');

        //该no-store字段不缓存
        // res.setHeader('Cache-Control', 'no-store');

        //返回静态目录的`/public/index.html`文件作为前端页面
        res.end(result);
      } else {
        //目录
        res.statusCode = 404;
        res.end('Not Found');
      }
    } catch (e) {
      res.statusCode = 404;
      res.end('Not Found');
    }
  }
}

module.exports = NodeServer;
